package my.netty.concurrent;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean;

public abstract class SingleThreadEventExecutor implements EventExecutor {

    private static final Logger log = LoggerFactory.getLogger(SingleThreadEventExecutor.class);

    private final EventExecutorGroup parent;
    private final ThreadFactory threadFactory;
    private final Queue<Runnable> taskQueue;

    private Thread thread;
    private final AtomicBoolean start = new AtomicBoolean(false);

    public SingleThreadEventExecutor(EventExecutorGroup parent, ThreadFactory threadFactory) {
        this.parent = parent;
        this.threadFactory = threadFactory;
        this.taskQueue = new ArrayBlockingQueue<>(10);
    }

    public boolean inEventLoop(Thread thread) {
        return this.thread == thread;
    }

    @Override
    public void execute(Runnable command) {
        if (command == null) {
            throw new NullPointerException("task is null");
        }
        addTask(command);
        if (!start.compareAndSet(false, true)) {
            return;
        }
        this.thread = threadFactory.newThread(SingleThreadEventExecutor.this::run);;
        this.thread.start();
    }

    private void addTask(Runnable task) {
        if (!taskQueue.offer(task)) {
            reject(task);
        }
    }

    protected boolean hasTasks() {
        return !this.taskQueue.isEmpty();
    }

    private void reject(Runnable task) {

    }

    protected void runAllTasks() {
        for (;;) {
            Runnable task = this.taskQueue.poll();
            if (task == null) {
                return;
            }
            safeExecute(task);
        }
    }

    protected void safeExecute(Runnable task) {
        try {
            task.run();
        } catch (Throwable t) {
            log.warn("A task raised an exception. Task: {}", task, t);
        }
    }

    protected abstract void run();
}
